﻿using AuthCenter.Data.Database;
using AuthCenter.Token;
using AuthCenter.Tools.Password;
using MySqlX.XDevAPI;
using SanlieHash;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TouchSocket.Http;

namespace AuthCenter.CSM.Common
{
    internal class AuthCenterSession : IDisposable
    {
        internal AuthDB db_auth { get; set; }
        internal AuthDeviceDB db_device { get; set; }
        internal AuthLogDB db_log { get; set; }
        internal IHttpSessionClient client { get; }
        internal HttpContextEventArgs e { get; }
        internal HttpRequest request { get; set; }
        internal HttpResponse response { get; set; }
        public AuthCenterSession(IHttpSessionClient client, HttpContextEventArgs e)
        {
            db_auth = new AuthDB();
            db_device = new AuthDeviceDB();
            db_log = new AuthLogDB();
            this.client = client;
            this.e = e;
            this.request = e.Context.Request;
            this.response = e.Context.Response;
            AppID = Guid.Empty;
            if (request.Headers.ContainsKey(HttpCommon.APPID) && Guid.TryParse(request.Headers[HttpCommon.APPID], out Guid appId))
            {
                AppID = appId;
            }
            else if (request.Query.ContainsKey(HttpCommon.P_app) && Guid.TryParse(request.Query[HttpCommon.P_app], out appId))
            {
                AppID = appId;
            }
            OnlineID = Guid.Empty;
            if (request.Headers.ContainsKey(HttpCommon.OID) && Guid.TryParse(request.Headers[HttpCommon.OID], out Guid oid))
            {
                OnlineID = oid;
            }
            else if (request.Query.ContainsKey(HttpCommon.P_oid) && Guid.TryParse(request.Query[HttpCommon.P_oid], out oid))
            {
                OnlineID = oid;
            }
            this.IsAuthenticated = false;
            this.IsAppAuthenticated = false;
            DeviceName = string.Empty;
            DeviceType = string.Empty;
            IP = client.IP;
            if (request.Headers.ContainsKey(HttpCommon.APPKEY))
            {
                this.AppKey = request.Headers[HttpCommon.APPKEY];
            }
            if (request.Headers.ContainsKey(HttpCommon.DEVICEDESC))
            {
                DeviceName = request.Headers[HttpCommon.DEVICEDESC];
            }
            if (this.AppKey != null && request.Headers.ContainsKey(HttpCommon.APPSRCDESC))
            {
                DeviceType = request.Headers[HttpCommon.APPSRCDESC];
            }
            VerifyApp();
            VerifyToken();
        }
        public string IP { get; protected set; }
        public Guid AppID { get; protected set; }
        public Guid OnlineID { get; protected set; }
        public Guid UserID { get; protected set; }
        public bool IsAuthenticated { get; protected set; }
        public string DeviceName { get; protected set; }
        public string DeviceType { get; protected set; }
        public string? AppKey { get; protected set; }
        public bool IsAppAuthenticated { get; protected set; }

        private void VerifyApp()
        {
            if (string.IsNullOrEmpty(this.AppKey)) return;
            //应用校验
            var appPassword = db_auth.GetAppPassword(this.AppID);
            if (appPassword == null || !PasswordCheck.Vertify(appPassword.AppPass, this.AppKey, appPassword.AppPassWay))
            {
                this.IsAppAuthenticated = true;
            }
        }
        private void VerifyToken()
        {
            if (request.Headers.ContainsKey(HttpCommon.TOKEN) && OnlineID != Guid.Empty)
            {
                //校验设备
                var deviceType = db_device.ReadOnlineDeviceType(OnlineID);
                if (!string.IsNullOrEmpty(deviceType))
                {
                    if (!string.IsNullOrEmpty(this.AppKey))
                    {
                        if (this.IsAppAuthenticated && deviceType == DeviceType && CheckToken())
                        {
                            this.IsAuthenticated = true;
                        }
                    }
                    else
                    {
                        if (deviceType == FastHash.GetMD5(DeviceName + IP) && CheckToken())
                        {
                            this.IsAuthenticated = true;
                        }
                    }
                }
            }
        }
        public bool CheckToken()
        {
            string token = string.Empty;
            //登录设备访问
            if (request.Headers.ContainsKey(HttpCommon.TOKEN))
            {
                token = request.Headers[HttpCommon.TOKEN];
            }
            else if (request.Query.ContainsKey(HttpCommon.P_auth))
            {
                token = request.Query[HttpCommon.P_auth];
            }

            var scheme = db_auth.GetAppScheme(AppID);
            if (GlobalToken.ReadScheme(token) == scheme)
            {
                var pass = db_device.GetTokenKey(OnlineID);
                var token_inner = GlobalToken.ReadToken(token);
                if (token_inner != string.Empty && pass != null && /*校验过期*/pass.ExpTime > DateTime.UtcNow && !string.IsNullOrEmpty(pass.Secret))
                {
                    var bag = GlobalToken.GetOnlineID(token_inner, pass.Secret);
                    if (bag.OnlineID == OnlineID)
                    {
                        this.OnlineID = bag.OnlineID;
                        this.UserID = bag.UserID;
                        return true;
                    }
                }
            }
            return false;
        }
        public async Task<T> GetData<T>() where T : class
        {
            return await request.GetDataAsync<T>();
        }

        public bool IsPost(string uri)
        {
            return request.IsPost() && request.UrlEquals(uri);
        }
        public bool IsGet(string uri)
        {
            return request.IsGet() && request.UrlEquals(uri);
        }

        public Task InvokeNext()
        {
            return e.InvokeNext();
        }

        public HttpResponse Send(int status, object obj, string message = "success")
        {
            return response
                        .SetStatus(status, message)
                        .FromObj(obj);
        }

        public void Dispose()
        {
            db_auth.Dispose();
            db_device.Dispose();
            db_log.Dispose();
        }
    }
}
